﻿using System;
using System.IO;
using Windows.Foundation;
using Windows.Storage.Streams;

namespace Smile.Compatibility
{
    public class MemoryRandomAccessStream : MemoryStream, IRandomAccessStream
    {
        // @Properties
        /// <summary>Gets the current position within the stream.</summary>
        ///
        /// <value>The current position within the stream.</value>
        public new ulong Position
        {
            get 
            {
                return (ulong)base.Position;
            }
            set
            {
                base.Position = (long)value;
            }
        }

        /// <summary>Gets or sets the size of the random access stream.</summary>
        ///
        /// <value>The size of the stream.</value>
        public ulong Size
        {
            get
            {
                return (ulong)Length;
            }
            set
            {
                SetLength((long)value);
            }
        }

        // @Public
        /// <summary>
        ///     Initializes a new instance of the MemoryRandomAccessStream class with an
        ///     expandable capacity initialized to zero.
        /// </summary>
        public MemoryRandomAccessStream()
        {
        }

        /// <summary>
        ///     Initializes a new non-resizable instance of the MemoryRandomAccessStream class
        ///     based on the specified byte array.
        /// </summary>
        ///
        /// <param name="buffer">
        ///     The array of unsigned bytes from which to create the current stream.
        /// </param>
        public MemoryRandomAccessStream(byte[] buffer) 
            : base(buffer)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the MemoryRandomAccessStream class with an
        ///     expandable capacity initialized as specified.
        /// </summary>
        ///
        /// <param name="capacity">The initial size of the internal array in bytes.</param>
        public MemoryRandomAccessStream(int capacity) 
            : base(capacity)
        {
        }

        /// <summary>
        ///     Initializes a new non-resizable instance of the MemoryRandomAccessStream class
        ///     based on the specified byte array with the CanWrite property set as specified.
        /// </summary>
        ///
        /// <param name="buffer">
        ///     The array of unsigned bytes from which to create this stream.
        /// </param>
        /// <param name="writable">
        ///     The setting of the CanWrite property, which determines whether the stream
        ///     supports writing.
        /// </param>
        public MemoryRandomAccessStream(byte[] buffer, bool writable)
            : base(buffer, writable)
        {
        }

        /// <summary>
        ///     Initializes a new non-resizable instance of the MemoryRandomAccessStream class
        ///     based on the specified region (index) of a byte array.
        /// </summary>
        ///
        /// <param name="buffer">
        ///     The array of unsigned bytes from which to create this stream.
        /// </param>
        /// <param name="index"> The index into buffer at which the stream begins.</param>
        /// <param name="count"> The length of the stream in bytes.</param>
        public MemoryRandomAccessStream(byte[] buffer, int index, int count)
            : base(buffer, index, count)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the MemoryRandomAccessStream class based on the
        ///     specified region of a byte array, with the CanWrite property set as specified,
        ///     and the ability to call GetBuffer set as specified.
        /// </summary>
        ///
        /// <param name="buffer">
        ///     The array of unsigned bytes from which to create this stream.
        /// </param>
        /// <param name="index">   The index into buffer at which the stream begins.</param>
        /// <param name="count">   The length of the stream in bytes.</param>
        /// <param name="writable">
        ///     The setting of the CanWrite property, which determines whether the stream
        ///     supports writing.
        /// </param>
        public MemoryRandomAccessStream(byte[] buffer, int index, int count, bool writable)
            : base(buffer, index, count, writable)
        {
        }

        /// <summary>
        ///     Creates a new instance of a IRandomAccessStream over the same resource as the
        ///     current stream.
        /// </summary>
        ///
        /// <returns>
        ///     The new stream. The initial, internal position of the stream is 0.The internal
        ///     position and lifetime of this new stream are independent from the position and
        ///     lifetime of the cloned stream.
        /// </returns>
        public IRandomAccessStream CloneStream()
        {
            MemoryRandomAccessStream clone = new MemoryRandomAccessStream(Capacity);
            CopyTo(clone);
            return clone;
        }

        /// <summary>Returns an input stream at a specified location in a stream.</summary>
        ///
        /// <param name="position">The current position within the stream.</param>
        ///
        /// <returns>The input stream.</returns>
        public IInputStream GetInputStreamAt(ulong position)
        {
            Seek((long)position, SeekOrigin.Begin);
            return this.AsInputStream();
        }

        /// <summary>Returns an output stream at a specified location in a stream.</summary>
        ///
        /// <param name="position">The current position within the stream.</param>
        ///
        /// <returns>The output stream.</returns>
        public IOutputStream GetOutputStreamAt(ulong position)
        {
            Seek((long)position, SeekOrigin.Begin);
            return this.AsOutputStream();
        }

        /// <summary>
        ///     Sets the position within the current stream to the specified value.
        /// </summary>
        ///
        /// <param name="position">The current position within the stream.</param>
        public void Seek(ulong position)
        {
            Seek((long)position, 0);
        }

        /// <summary>
        ///     Asynchronously reads a sequence of bytes from the current stream and advances the
        ///     position within the stream by the number of bytes read.
        /// </summary>
        ///
        /// <param name="buffer"> The buffer from which to create the current stream.</param>
        /// <param name="count">  The length of the stream in bytes.</param>
        /// <param name="options">Options for controlling the operation.</param>
        ///
        /// <returns>A task that represents the asynchronous read operation.</returns>
        public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
        {
            var inputStream = GetInputStreamAt(0);
            return inputStream.ReadAsync(buffer, count, options);
        }

        /// <summary>
        ///     Asynchronously clears all buffers for this stream and causes any buffered data to
        ///     be written to the underlying device.
        /// </summary>
        ///
        /// <returns>
        ///     A IAsyncOperation that represents the asynchronous flush operation.
        /// </returns>
        public new IAsyncOperation<bool> FlushAsync()
        {
            var outputStream = GetOutputStreamAt(0);
            return outputStream.FlushAsync();
        }

        /// <summary>
        ///     Asynchronously writes a sequence of bytes to the current stream and advances the
        ///     current position within this stream by the number of bytes written.
        /// </summary>
        ///
        /// <param name="buffer">The buffer from which to create the current stream.</param>
        ///
        /// <returns>
        ///     A IAsyncOperationWithProgress that represents the asynchronous write operation.
        /// </returns>
        public IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer)
        {
            var outputStream = GetOutputStreamAt(0);
            return outputStream.WriteAsync(buffer);
        }
    }
}
